﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using Borg.Framework.CompositionBinding;

namespace Borg.Forms.Framework.CompositionBinding
{
    public class CompositionListControl<T> : ICompositionControl<T>
        where T : class
    {
        private T _composedItem;
        private readonly ListControl _control;
        private FormatHandler<T> _formatHandler;
        private readonly BindingList<T> _items;
        private bool _noUpdate;

        public IEnumerable<T> ComposedItems
        {
            get
            {
                if (_composedItem == null)
                    yield break;

                yield return _composedItem;
            }
            set
            {
                foreach (var local in value)
                {
                    SetComposed(local);
                    break;
                }
            }
        }

        public FormatHandler<T> FormatHandler
        {
            get { return _formatHandler; }
            set
            {
                if (_formatHandler == value)
                    return;

                _formatHandler = value;

                if (_formatHandler != null)
                {
                    _control.Format += FormatItem;
                    _control.FormattingEnabled = true;
                }
                else
                    _control.FormattingEnabled = false;
            }
        }

        public IEnumerable<T> Items
        {
            get { return _items; }
            set
            {
                _items.Clear();

                foreach (var local in value)
                    _items.Add(local);
            }
        }

        public event EventHandler<ItemEventArgs<T>> ComposeItem;
        public event EventHandler<ItemEventArgs<T>> DecomposeItem;

        public CompositionListControl(ListControl control)
        {
            _items = new BindingList<T>();
            _control = control;
            control.SelectedValueChanged += SelectedValueChanged;
            _control.DataSource = _items;
        }

        public CompositionListControl(ListControl control, FormatHandler<T> formatHandler)
            : this(control)
        {
            FormatHandler = formatHandler;
        }

        public void Add(T item)
        {
            _noUpdate = true;

            var selectedIndex = _control.SelectedIndex;

            _items.Add(item);
            _control.SelectedIndex = selectedIndex;
            _noUpdate = false;
        }

        public void Clear()
        {
            _items.Clear();
        }

        private void Deselect()
        {
            var bindingContext = _control.BindingContext;

            if (bindingContext != null)
                bindingContext[_items].Position = 0;

            _control.SelectedIndex = -1;
        }

        private void FormatItem(object sender, ListControlConvertEventArgs e)
        {
            e.Value = _formatHandler((T)e.ListItem);
        }

        public void Remove(T item)
        {
            _noUpdate = true;

            if (_control.SelectedValue == item)
                Deselect();

            var selectedIndex = _control.SelectedIndex;
            var index = _items.IndexOf(item);

            _items.Remove(item);

            _control.SelectedIndex =
                selectedIndex > index
                ? selectedIndex - 1
                : selectedIndex;

            _noUpdate = false;
        }

        private void SelectedValueChanged(object sender, EventArgs e)
        {
            if (_noUpdate || _composedItem == _control.SelectedValue)
                return;

            _noUpdate = true;
            var composedItem = _composedItem;
            _composedItem = (T)_control.SelectedValue;

            if (composedItem != null && DecomposeItem != null)
                DecomposeItem(this, new ItemEventArgs<T>(composedItem));

            if (_composedItem != null && ComposeItem != null)
                ComposeItem(this, new ItemEventArgs<T>(_composedItem));

            _noUpdate = false;
        }

        public void SetComposed(T item)
        {
            if (_noUpdate || _composedItem == item)
                return;

            _noUpdate = true;
            _composedItem = item;
            _control.SelectedIndex = _items.IndexOf(item);
            _noUpdate = false;
        }

        public void SetDecomposed(T item)
        {
            if (_noUpdate || item != _composedItem)
                return;

            _noUpdate = true;
            _composedItem = default(T);
            Deselect();
            _noUpdate = false;
        }
    }
}

